//! Database integration tests

use ai_test_platform::{
    storage::{
        DatabaseStorage, RedisCache, TestStorage, TestCache,
        DatabaseConfig, CacheConfig, StorageConfig, DatabaseStorageManager,
    },
    models::{
        testcase::TestCase,
        config::TestConfig,
        step::{StepResult, StepType},
        Value,
    },
};
use chrono::Utc;
use std::time::Duration;
use uuid::Uuid;

#[tokio::test]
async fn test_database_storage_creation() {
    // Skip if no database URL is provided
    if std::env::var("DATABASE_URL").is_err() {
        println!("Skipping database test - DATABASE_URL not set");
        return;
    }

    let config = DatabaseConfig::from_env();
    let result = DatabaseStorage::new(&config.database_url).await;
    
    // This might fail if database is not available, which is OK for CI
    match result {
        Ok(_storage) => {
            println!("Database storage created successfully");
        }
        Err(e) => {
            println!("Database storage creation failed (expected in CI): {}", e);
        }
    }
}

#[tokio::test]
async fn test_redis_cache_creation() {
    // Skip if no Redis URL is provided
    if std::env::var("REDIS_URL").is_err() {
        println!("Skipping Redis test - REDIS_URL not set");
        return;
    }

    let config = CacheConfig::from_env();
    let result = RedisCache::new(&config.redis_url).await;
    
    // This might fail if Redis is not available, which is OK for CI
    match result {
        Ok(_cache) => {
            println!("Redis cache created successfully");
        }
        Err(e) => {
            println!("Redis cache creation failed (expected in CI): {}", e);
        }
    }
}

#[tokio::test]
async fn test_storage_config_creation() {
    let config = StorageConfig::from_env();
    
    // Test that config is created with reasonable defaults
    assert!(!config.database.database_url.is_empty());
    assert!(!config.cache.redis_url.is_empty());
    assert!(config.database.max_connections > 0);
    assert!(config.cache.default_ttl > 0);
}

#[tokio::test]
async fn test_database_config_from_env() {
    let config = DatabaseConfig::from_env();
    
    // Test default values
    assert!(config.database_url.contains("postgresql") || config.database_url.contains("postgres"));
    assert_eq!(config.max_connections, 10);
    assert_eq!(config.connection_timeout, 30);
    assert_eq!(config.idle_timeout, 600);
}

#[tokio::test]
async fn test_cache_config_from_env() {
    let config = CacheConfig::from_env();
    
    // Test default values
    assert!(config.redis_url.contains("redis"));
    assert_eq!(config.default_ttl, 3600);
    assert_eq!(config.max_connections, 10);
    assert_eq!(config.connection_timeout, 5);
}

#[tokio::test]
async fn test_storage_manager_creation() {
    // Test that storage manager can be created with mock configuration
    let config = StorageConfig::default();
    
    // This will likely fail without actual database/Redis, but we test the config
    let result = DatabaseStorageManager::new(config).await;
    
    match result {
        Ok(_manager) => {
            println!("Storage manager created successfully");
        }
        Err(e) => {
            println!("Storage manager creation failed (expected without services): {}", e);
            // This is expected in CI environment without database/Redis
        }
    }
}

#[tokio::test]
async fn test_test_case_serialization() {
    use ai_test_platform::models::testcase::{TestCaseMetadata, TestPriority};
    use ai_test_platform::models::now;

    // Test that TestCase can be serialized/deserialized for database storage
    let test_case = TestCase {
        id: Uuid::new_v4(),
        config: TestConfig::new("test_config".to_string()),
        test_steps: vec![], // Empty steps for testing
        created_at: now(),
        updated_at: now(),
        metadata: TestCaseMetadata {
            author: Some("test_author".to_string()),
            description: Some("Test description".to_string()),
            tags: vec!["api".to_string(), "integration".to_string()],
            category: Some("integration".to_string()),
            priority: TestPriority::High,
            enabled: true,
            version: "1.0.0".to_string(),
        },
    };

    // Test JSON serialization
    let json = serde_json::to_value(&test_case).unwrap();
    assert!(json.is_object());
    assert_eq!(json["metadata"]["description"], "Test description");
    assert_eq!(json["metadata"]["priority"], "High");

    // Test deserialization
    let deserialized: TestCase = serde_json::from_value(json).unwrap();
    assert_eq!(deserialized.metadata.description, test_case.metadata.description);
    assert_eq!(deserialized.metadata.priority, test_case.metadata.priority);
}

#[tokio::test]
async fn test_step_result_serialization() {
    // Test that StepResult can be serialized/deserialized for database storage
    let step_result = StepResult::new(
        Uuid::new_v4(),
        "test_step".to_string(),
        StepType::API,
    ).success();

    // Test JSON serialization
    let json = serde_json::to_value(&step_result).unwrap();
    assert!(json.is_object());
    assert_eq!(json["name"], "test_step");
    assert_eq!(json["success"], true);

    // Test deserialization
    let deserialized: StepResult = serde_json::from_value(json).unwrap();
    assert_eq!(deserialized.name, step_result.name);
    assert_eq!(deserialized.success, step_result.success);
}

#[tokio::test]
async fn test_cache_value_serialization() {
    // Test that cache values can be serialized/deserialized
    let test_values = vec![
        Value::String("test_string".to_string()),
        Value::Number(serde_json::Number::from(42)),
        Value::Bool(true),
        Value::Array(vec![Value::String("item1".to_string()), Value::String("item2".to_string())]),
        Value::Object({
            let mut map = serde_json::Map::new();
            map.insert("key1".to_string(), Value::String("value1".to_string()));
            map.insert("key2".to_string(), Value::Number(serde_json::Number::from(123)));
            map
        }),
    ];

    for value in test_values {
        // Test serialization
        let serialized = serde_json::to_string(&value).unwrap();
        assert!(!serialized.is_empty());

        // Test deserialization
        let deserialized: Value = serde_json::from_str(&serialized).unwrap();
        assert_eq!(deserialized, value);
    }
}

#[tokio::test]
async fn test_database_error_handling() {
    // Test error handling for invalid database URL
    let invalid_url = "invalid://database/url";
    let result = DatabaseStorage::new(invalid_url).await;
    
    assert!(result.is_err());
    let error = result.unwrap_err();
    assert!(error.to_string().contains("Failed to connect to database") || 
            error.to_string().contains("Connection failed"));
}

#[tokio::test]
async fn test_cache_error_handling() {
    // Test error handling for invalid Redis URL
    let invalid_url = "invalid://cache/url";
    let result = RedisCache::new(invalid_url).await;
    
    assert!(result.is_err());
    let error = result.unwrap_err();
    assert!(error.to_string().contains("Failed to create Redis") || 
            error.to_string().contains("Connection failed"));
}

#[tokio::test]
async fn test_storage_health_check() {
    // Test storage health check structure
    let config = StorageConfig::default();
    
    // This will likely fail without actual services, but we test the structure
    if let Ok(manager) = DatabaseStorageManager::new(config).await {
        let health = manager.health_check().await.unwrap();
        
        // Health check should return a valid structure
        assert!(health.overall == (health.database && health.cache));
    }
}
